#pragma once
# include <string>
# include <sstream> 
# include <thread>
# include <vector>
# include "Enums.h"


namespace ZDSJ {
	class Formater {
	public:
		Formater();

		Formater(const std::string& _formater);

		template <class ... Args>
		std::string writeLog(std::string _time, std::string _level, std::string _file_name, std::string _func_name, int _line, std::thread::id _thread_id, Args && ... _args) {
			std::string result(*this->m_formater);
			std::string message = this->format(_args...);
			
			for (auto item : *this->m_payload) {
				switch (item)
				{
				case Support::time:
					result = this->format(result, _time);
					break;
				case Support::level:
					result = this->format(result, _level);
					break;
				case Support::file:
					result = this->format(result, _file_name);
					break;
				case Support::func:
					result = this->format(result, _func_name);
					break;
				case Support::line:
					result = this->format(result, _line);
					break;
				case Support::thread:
					result = this->format(result, _thread_id);
					break;
				case Support::message:
					result = this->format(result, message);
					break;
				default:
					break;
				}
			}
			return std::move(result);
		}

		template <class ... Args>
		std::string operator()(std::string _time, std::string _level, std::string _file_name, std::string _func_name, int _line, std::thread::id _thread_id, Args && ... _args) {
			return this->writeLog(_time, _level, _file_name, _func_name, _line, _thread_id, _args...);
		}

		Formater(const Formater& _other);
		Formater& operator=(const Formater& _other);

		~Formater();
	private:
		std::string* m_formater = nullptr;
		std::vector<Support>* m_payload = nullptr;

		void dealFormat();

		template <class ... Args>
		std::string format(Args&... _args) {
			std::ostringstream oss;
			this->format(oss, _args...);
			return std::move(oss.str());
		}

		template <class T, class... Args>
		void format(std::ostringstream& _oss, T& _format, Args&... _args) {
			this->format(_oss, _format, 0, _args...);
		}

		void format(std::ostringstream& _oss, const std::string& _format, std::size_t _offset)
		{
			_oss << _format.substr(_offset, _format.size() - _offset);
		}

		template <class T, class... Args>
		void format(
			std::ostringstream& _oss, const std::string& _format, std::size_t _offset, T& _first, Args&... _args)
		{
			std::size_t off = _format.find("{}", _offset);
			if (off == std::string::npos) {
				_oss << _format.substr(_offset, _format.size() - _offset);
				return;
			}
			_oss << _format.substr(_offset, off - _offset) << _first;
			this->format(_oss, _format, off + 2, _args...);
		}

	};
}